/**
 * Air compressor ctrol system
 * ©by ltree
 */

/*------------------------------------------------------------------------------
Includes */
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/*------------------------------------------------------------------------------
Private includes*/
/* USER CODE BEGIN Includes */
#include "usart.h"
#include "shell_port.h"
#include "mdslave.h"
#include "io_signal.h"
#include "dwin.h"
#include "adc.h"
#include "tool.h"
#include "Flash.h"
#include "at_port.h"
#include "pid.h"
#include "Air_compressor.h"
/* USER CODE END Includes */

/*------------------------------------------------------------------------------
Private Macros*/
#ifndef container_of
#define container_of(ptr, type, member)                                 \
    ({                                                                  \
        const __typeof__(((type *) NULL)->member) *__mptr = (ptr);      \
        (type *) (void*) (((char *)__mptr) - offsetof(type, member));   \
    })
#endif

#define OP_FUN \
    OP_FUN_DEF(RISING, Rising_pressure)\
    OP_FUN_DEF(REDUCE, Reduce_pressure)\
    OP_FUN_DEF(HOLDING, Holding_pressure)\

typedef enum {
#define OP_FUN_DEF(a,b) a,
   OP_FUN
#undef OP_FUN_DEF
   OP_MAX
} tOp;

typedef void (*op_func)(void);

/*------------------------------------------------------------------------------
Declaration Function*/
void FalseDebug(mdSTATUS ret);
void check_alarm(void);
void reset_alarm(void);
void cmpsr_check_startup(void);
void buzzer(void);

void open_air_cmpsr(uint8_t id);

/*------------------------------------------------------------------------------
Private Vars*/
cmpsr_dev_t cmpsr_devsHead;
cmpsr_dev_t cmpsr_devs[cmpsr_num];


float alarm_air_pressure_list[ALARM_NUM * 2] = {0};
float air_pressure_list[ADC_DMA_CHANNEL];
uint8_t alarm_code = 0x0;
alarm_t alarm_list =
{
   .status = {0},
   .mdreg_addr = {0, 2, 4, 6, 8, 10, 12, 14},
   .alarm_action = buzzer,
};

PID_State cmpsr_pid;

op_func op_funcs[] = {
#define OP_FUN_DEF(a,b) b,
   OP_FUN
#undef OP_FUN_DEF
};

/*------------------------------------------------------------------------------
Private Functions*/
void cmpsr_initialize(void)
{
   cmpsr_dev_initialize();
}



void cmpsr_ctrl(ctrl_fun_t hook, void* pVar)
{
   return;
}



void Rising_pressure(void)
{
   return;
}



void Reduce_pressure(void)
{
   return;
}



void Holding_pressure(void)
{
   return;
}



void cmpsr_alarm(void)
{
   mdSTATUS ret = mdRTU_ReadInputRegisters(
                     Slave_Object,
                     INPUT_ANALOG_START_ADDR,
                     ADC_DMA_CHANNEL * 2U,
                     (mdU16 *)air_pressure_list);

   ret = mdRTU_ReadHoldRegisters(
            Slave_Object,
            PARAM_MD_ADDR,
            ALARM_NUM * 4U,
            (mdU16*)alarm_air_pressure_list);

   osDelay(10);
   FalseDebug(ret);

   cmpsr_check_startup();
   check_alarm();
   reset_alarm();

   static uint8_t first = 1U;

   if (first)
   {
      first = 0U;
      for (int i = 0; i < ALARM_NUM; ++i)
      {
         mdRTU_WriteCoil(Slave_Object, ALARM_RESET_ADDR + i, mdFALSE);
         mdRTU_WriteCoil(Slave_Object, 0, mdFALSE);

      }
   }

   return;
}



void reset_alarm(void)
{
   uint8_t reset = 0U;
   mdRTU_ReadCoil(Slave_Object, ALARM_RESET_ADDR - 1, reset);

   if (reset > 0)
   {
      for (int i = 0; i < ALARM_NUM; ++i)
      {
         mdRTU_WriteCoil(Slave_Object, ALARM_RESET_ADDR + i, mdFALSE);
      }
   }
}



void check_alarm(void)
{
   for (int i = 0, n = 0 ; n < ALARM_NUM; i += 2, ++n)
   {

      if (air_pressure_list[n] <= alarm_air_pressure_list[i]
            && air_pressure_list[n] > alarm_air_pressure_list[i + 1])
      {
         // close alarm flag
         alarm_list.reset[n] = mdFALSE;
         mdRTU_WriteCoil(Slave_Object, ALARM_RESET_ADDR + n, mdFALSE);
         mdRTU_WriteCoil(Slave_Object, ALARM_COILS_ADDR + n, mdFALSE);

      }
      else
      {
         if (alarm_list.reset[n] == mdFALSE)
         {
            // open alarm flag0
            mdRTU_WriteCoil(Slave_Object, ALARM_COILS_ADDR + n, mdTRUE);
            mdRTU_WriteCoil(Slave_Object, ALARM_RESET_ADDR + n, mdTRUE);

            alarm_list.reset[n] = mdTRUE;
         }
      }
   }

   buzzer();
}



void cmpsr_check_startup(void)
{
   for (int n = 0 ; n < ALARM_NUM; ++n)
   {
      if (air_pressure_list[n] > 0.1F)
      {
         mdRTU_WriteCoil(Slave_Object, CMPSR_START_ADDR + n, mdTRUE);
      }
      else
      {
         mdRTU_WriteCoil(Slave_Object, CMPSR_START_ADDR + n, mdFALSE);
      }
   }
}



void cmpsr_alarm_set(void)
{
   alarm_air_pressure_e index;
   float value;
   Save_HandleTypeDef *ps = &Save_Flash;

   float* list = (float*)&ps->Param.alarm_limit_0;



   for (;;)
   {
      for (uint8_t n = 0; n < 2 * ALARM_NUM; ++n)
      {
         SHELL_PUT("%f \r\n", list[n]);
      }
      SHELL_PUT("press ESC to exit, or Enter to continue enter your set"
                "[index:value].\r\n");
      if (ESC_CODE == checkShellPress()) break;

      SHELL_PUT("enter your set\r\n>[index:value]:");
      shellScan(Shell_Object, "%u:%f", (char*)&index, &value);
      list[index] = value;
   }
   return;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
                 alarmset, cmpsr_alarm_set, config);



uint16_t alarm2dwin(void)
{
   uint16_t buf = 0;
   for (uint8_t n = 0 ; n < ALARM_NUM; ++n)
   {
      uint8_t bit;
      mdRTU_ReadCoil(Slave_Object, ALARM_RESET_ADDR + n, bit);
      buf |= bit << (n + 8U);
   }

   return buf;
}



void see_alarm_radius(void)
{
   Save_HandleTypeDef *ps = &Save_Flash;
   float *value = &ps->Param.alarm_limit_0;
   for (uint8_t n = 0; n < 17; ++n)
   {
      shellPrint(Shell_Object, "%f \r\n", *(value++));
   }
   value = NULL;
   return;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
                 seealarm, see_alarm, read);



void FalseDebug(mdSTATUS ret)
{
   if (ret == mdFALSE)
   {
#if defined(USING_DEBUG)
      shellPrint(Shell_Object, "ReadInputRegisters error. ret:%d\r\n", ret);
#endif
   }

   return;
}



void buzzer(void)
{
   //send buzzer status to dwin
   //send buzzer status to modbusTCP
   uint8_t op = 0;
   for (int i = 0; i < ALARM_NUM; ++i)
   {
      uint8_t tmp;
      mdRTU_ReadCoil(Slave_Object, ALARM_RESET_ADDR + i, tmp);
      op += tmp;
   }

   if (op > 0) mdRTU_WriteCoil(Slave_Object, 0, mdTRUE);
   else mdRTU_WriteCoil(Slave_Object, 0, mdFALSE);
}



void io_ctrl(uint16_t io, uint8_t op)
{
   return;
}



void time_average_ctrl(cmpsr_dev_p dev)
{
   cmpsr_devsHead.next->usedTime++;

   return;
}



void cot_average_ctrl(cmpsr_dev_p dev)
{
   cmpsr_devsHead.next->usedCot++;

   return;
}



void cmpsr_dev_initialize(void)
{
   cmpsr_devsHead.node.last = &cmpsr_devsHead;
   cmpsr_devsHead.dev.num = 0;

   for (int i = cmpsr_1; i < cmpsr_num; ++i)
   {
      add_cmpsr_dev(&cmpsr_devs[i]);
   }

   return;
}



cmpsr_dev_p find_cmpsr_dev(uint8_t dev_id)
{
   cmpsr_dev_p pRet = cmpsr_devsHead.next;

   for (; pRet != NULL;)
   {

      if (pRet->dev.id == dev_id)
         return pRet;
      ;

      pRet = pRet->next;
   }

   return NULL;
}



cmpsr_dev_p newcmpsr(void)
{
   cmpsr_dev_p new = (cmpsr_dev_p)pvPortMalloc(sizeof(cmpsr_dev_t));
   add_cmpsr_dev(new);
   return new;
}



uint8_t add_cmpsr_dev(cmpsr_dev_p new)
{
   if (new == NULL) {return 0;}

   new->node.prev = cmpsr_devsHead.node.last;
   new->dev.id = cmpsr_devsHead.dev.num++;
   cmpsr_devsHead.node.last->next = new;
   cmpsr_devsHead.node.last = new;
   return 1;
}



uint8_t del_cmpsr_dev(uint8_t dev_id)
{
   cmpsr_dev_p find = find_cmpsr_dev(dev_id);

   if (find == NULL) {
      return 0;
   }

   if (find == cmpsr_devsHead.node.last) {
      cmpsr_devsHead.node.last = find->node.prev;
      cmpsr_devsHead.node.last->next = NULL;
   } else {
      find->node.prev->next = find->next;
      find->next->node.prev = find->node.prev;
   }

   CUSTOM_FREE(find);

   return 1;
}




/*file end*/
